package org.gpp.proj1.communication.sender;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.UUID;

import org.gpp.proj1.communication.ConnectionException;
import org.gpp.proj1.communication.processor.RequestProcessorIF;
import org.gpp.proj1.communication.protocol.Message;
import org.gpp.proj1.communication.sender.manager.RequestSenderManagerIF;

/**
 * @author Paolo Victor, paolovictor@gmail.com
 */
public class RMIRequestSender implements RequestSenderIF {

	private String clientId = null;
	private String serverId = null;
	private RequestProcessorIF requestProcessor = null;
	
	public RMIRequestSender(String clientId, String serverId, RequestProcessorIF requestProcessor) {
		
		this.clientId = clientId;
		this.serverId = serverId;
		this.requestProcessor = requestProcessor;
	}

	/**
	 * @param manager The request sender manager that will be notified about the connection
	 * @param alias The client alias, a human readable name
	 * @param localPort The client's processor port
	 * @param serverAddress The server address
	 * @param serverPort The server port
	 * @return A request sender connected to the server
	 * @throws ConnectionException
	 */
	public static RequestSenderIF connectToServer(RequestSenderManagerIF manager, int localPort, String serverAddress, int serverPort)
			throws ConnectionException {

		String serverRMIAddress = "rmi://" + serverAddress + ":" + serverPort + "/" + RequestProcessorIF.REQUEST_PROCESSOR;
		try {
			RequestProcessorIF requestProcessor = (RequestProcessorIF) Naming.lookup( serverRMIAddress );
			
			String serverId = UUID.randomUUID().toString();
			
			String clientId = requestProcessor.connect( serverId, localPort );
			
			RMIRequestSender sender = new RMIRequestSender( clientId, serverId, requestProcessor );
			
			manager.notifyConnected( serverId, sender );
			
			return sender;
		} catch (MalformedURLException e) {
			throw new ConnectionException("Invalid server id");
		} catch (NotBoundException e) {
			throw new ConnectionException("Server request processor isn't bound");
		} catch (RemoteException e) {
			throw new ConnectionException("Error while trying to connect", e);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.communication.sender.RequestSenderIF#disconnect()
	 */
	public void disconnect() throws ConnectionException {

		checkConnected();
		
		try {			
			this.requestProcessor.disconnect( this.clientId );
			this.requestProcessor = null;
			this.clientId = null;
		} catch (RemoteException e) {
			throw new ConnectionException("Error while trying to disconnect", e);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.communication.sender.RequestSenderIF#ping()
	 */
	public void ping() throws ConnectionException {
		
		if( this.requestProcessor == null ) {
			throw new ConnectionException("Not connected to the server");
		}
		
		try {
			this.requestProcessor.ping();
		} catch (RemoteException e) {
			throw new ConnectionException("Error while trying to ping server", e);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.communication.sender.RequestSenderIF#send(org.gpp.proj1.communication.protocol.Message)
	 */
	public void send( Message message ) throws ConnectionException {
		
		checkConnected();
		
		message.setSenderId( this.clientId );
		
		try {			
			this.requestProcessor.receive( message );
		} catch (RemoteException e) {
			throw new ConnectionException("Could not send message: " + message, e);
		}
	}

	/**
	 * @throws ConnectionException If the sender isn't connected
	 */
	private void checkConnected() throws ConnectionException {
		
		if( requestProcessor == null ) {
			throw new ConnectionException( "Not connected" );
		}
		this.ping();
	}
	
	/* (non-Javadoc)
	 * @see org.gpp.proj1.communication.sender.RequestSenderIF#getServerId()
	 */
	public String getServerId() {
		
		return this.serverId;
	}
	
	/* (non-Javadoc)
	 * @see org.gpp.proj1.communication.sender.RequestSenderIF#getClientId()
	 */
	public String getClientId() {
		return this.clientId;
	}
}
